#ifndef __INET__H__
#define __INET__H__
#include "Platform.h"

#if FG_PLATFORM == FG_PLATFORM_WIN
#include <WinSock2.h>
#include <windows.h>
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_LINUX || FG_PLATFORM == FG_PLATFORM_ANDROID

#if FG_PLATFORM == FG_PLATFORM_APPLE
#include <libkern/OSByteOrder.h>
#endif

#include <netinet/in.h>
#ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#include <unistd.h>
#endif

#pragma pack(push, 1)

namespace FragrantGrass
{
	enum FG_NET_EVENT
	{
		FG_NET_EVENT_READING = 0x01,
		FG_NET_EVENT_WRITING = 0x02,
		FG_NET_EVENT_EOF = 0x10,
		FG_NET_EVENT_ERROR = 0x20,
		FG_NET_EVENT_TIMEOUT = 0x40,
		FG_NET_EVENT_CONNECTED = 0x80,
	};

	class INet;
	typedef std::function<void(SOCK fd, int msgId, const char* msg, const uint32_t len)> NET_MESSAGE_HANDLER_FUNCTOR;
	typedef std::shared_ptr<NET_MESSAGE_HANDLER_FUNCTOR> NET_MESSAGE_HANDLER_FUNCTOR_PTR;
	typedef std::function<void(const SOCK fd,FG_NET_EVENT nEvent,INet* pNet)> NET_EVENT_HANDLER_FUNCTOR;
	typedef std::shared_ptr<NET_EVENT_HANDLER_FUNCTOR> NET_EVENT_HANDLER_FUNCTOR_PTR;

	struct IMsgHead
	{
		enum Head
		{
			HEAD_LENGTH = 6,
		};

		virtual int encode(char* data) = 0;

		virtual int decode(const char* data) = 0;

		virtual uint16_t get_msg_id() const = 0;

		virtual void set_msg_id(uint16_t msgId) = 0;

		virtual uint32_t get_body_length() const = 0;

		virtual void set_body_length(uint32_t len) = 0;

		static int64_t FG_HTONLL(int64_t data)
		{
#if FG_PLATFORM == FG_PLATFORM_WIN
			return htonll(data);
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_APPLE_IOS
			return OSSwapHostToBigInt64(data);
#else
			return htobe64(data);
#endif
		}

		static int64_t FG_NTOHLL(int64_t data)
		{
#if FG_PLATFORM == FG_PLATFORM_WIN
			return ntohll(data);
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_APPLE_IOS
			return OSSwapBigToHostInt64(data);
#elif FG_PLATFORM == FG_PLATFORM_ANDROID
			return betoh64(data);
#else
			return be64toh(data);
#endif
		}

		static int32_t FG_HTONL(int32_t data)
		{
#if FG_PLATFORM == FG_PLATFORM_WIN
			return htonl(data);
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_APPLE_IOS
			return OSSwapHostToBigInt32(data);
#else
			return htobe32(data);
#endif
		}

		static int32_t FG_NTOHL(int32_t data)
		{
#if FG_PLATFORM == FG_PLATFORM_WIN
			return ntohl(data);
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_APPLE_IOS
			return OSSwapBigToHostInt32(data);
#elif FG_PLATFORM == FG_PLATFORM_ANDROID
			return betoh32(data);
#else
			return be32toh(data);
#endif
		}

		static int16_t FG_HTONS(int16_t data)
		{
#if FG_PLATFORM == FG_PLATFORM_WIN
			return htons(data);
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_APPLE_IOS
			return OSSwapHostToBigInt16(data);
#else
			return htobe16(data);
#endif
		}

		static int16_t FG_NTOHS(int16_t data)
		{
#if FG_PLATFORM == FG_PLATFORM_WIN
			return ntohs(data);
#elif FG_PLATFORM == FG_PLATFORM_APPLE || FG_PLATFORM == FG_PLATFORM_APPLE_IOS
			return OSSwapBigToHostInt16(data);
#elif FG_PLATFORM == FG_PLATFORM_ANDROID
			return betoh16(data);
#else
			return be16toh(data);
#endif
		}
	};

	class MsgHead : public IMsgHead
	{
	public:
		MsgHead()
		{
			body_length = 0;
			msg_id = 0;
		}

		// Message Head[ MsgID(2) | MsgSize(4) ]
		virtual int encode(char* data)
		{
			uint32_t offset = 0;

			uint16_t msgId = FG_HTONS(msg_id);
			memcpy(data + offset, (void*)(&msgId), sizeof(msg_id));
			offset += sizeof(msgId);

			uint32_t pkg_length = body_length + HEAD_LENGTH;
			uint32_t pkg_size = FG_HTONL(pkg_length);
			memcpy(data + offset, (void*)(&pkg_size), sizeof(body_length));
			offset += sizeof(body_length);

			if (offset != HEAD_LENGTH)
			{
				assert(0);
			}

			return offset;
		}

		// Message Head[ MsgID(2) | MsgSize(4) ]
		virtual int decode(const char* data)
		{
			uint32_t offset = 0;

			uint16_t msgId = 0;
			memcpy(&msgId, data + offset, sizeof(msgId));
			msg_id = FG_NTOHS(msgId);
			offset += sizeof(msg_id);

			uint32_t pkg_length = 0;
			memcpy(&pkg_length, data + offset, sizeof(body_length));
			body_length = FG_NTOHL(pkg_length) - HEAD_LENGTH;
			offset += sizeof(body_length);

			if (offset != HEAD_LENGTH)
			{
				assert(0);
			}

			return offset;
		}

		virtual uint16_t get_msg_id() const
		{
			return msg_id;
		}

		virtual void set_msg_id(uint16_t nMsgID)
		{
			msg_id = nMsgID;
		}

		virtual uint32_t get_body_length() const
		{
			return body_length;
		}

		virtual void set_body_length(uint32_t nLength)
		{
			body_length = nLength;
		}

	protected:
		uint32_t body_length;
		uint16_t msg_id;
	};

	class NetHandle
	{
	public:
		NetHandle(INet* net, SOCK fd, sockaddr_in& addr, void* data)
		{
			m_pNet = net;
			m_fd = fd;
			m_addr = addr;
			m_data = data;
			m_buffer.clear();
			m_need_removed = false;
		}

		INet* getNet() const { return m_pNet; }

		int append(const char* str, size_t nLen)
		{
			m_buffer.append(str, nLen);
			return (int)m_buffer.length();
		}

		int remove(int pos, int len)
		{
			if (pos < 0 || len < 0 || pos + len > m_buffer.length())
			{
				return 0;
			}

			m_buffer.erase(pos, len);

			return (int)m_buffer.length();
		}

		int length() const
		{
			return (int)m_buffer.length();
		}

		inline int fd() const { return (int)m_fd; }

		inline const char* data() const
		{
			return m_buffer.data();
		}

		inline void* get_user_data() const { return m_data; }

		inline void* clear_user_data() { m_data = NULL; }

		inline void set_need_removed(bool need_removed)
		{
			m_need_removed = need_removed;
		}

		inline bool get_need_removed() const { return m_need_removed; }

	private:
		INet* m_pNet;
		SOCK m_fd;
		sockaddr_in m_addr;
		void* m_data;
		std::string m_buffer;
		bool m_need_removed;
	};

	class INet
	{
	public:
		enum NetFlags
		{
			NF_REMOVED = (1 << 0),
		};
	public:
		INet()
		{
			m_flag = 0;
		}

		virtual ~INet() {}
		virtual int init_as_client(const char* ip, unsigned short port) = 0;
		virtual int init_as_server(unsigned int maxClient, unsigned short port, int iCpuCount) = 0;
		virtual int expand_buffer_size(unsigned int size) = 0;

		virtual bool add_net_handle(SOCK fd, NetHandle* handle) = 0;
		virtual bool close_net_handle(SOCK fd) = 0;

		virtual bool execute() = 0;
		virtual bool destroy() = 0;

		virtual bool run_as_server() = 0;

		inline void set_need_removed(bool removed)
		{
			removed ? (m_flag &= ~NetFlags::NF_REMOVED) : (m_flag |= NetFlags::NF_REMOVED);
		}

		inline bool get_need_removed() const 
		{
			return (m_flag & NetFlags::NF_REMOVED) > 0;
		}

		virtual bool send_msg(const char* msg, uint32_t len,SOCK fd) = 0;
		virtual bool send_msg_with_out_head(int16_t id, const char* msg, uint32_t len, SOCK fd) = 0;
		virtual bool send_msg_to_all_client(const char* msg, uint32_t len) = 0;
		virtual bool send_msg_to_all_client_with_out_head(int16_t id, const char* msg, uint32_t len) = 0;
		virtual bool send_msg_to_specific_clients(const char* msg, uint32_t len,const std::list<SOCK>& clients) = 0;

	private:
		INT32 m_flag;

	};
}

#pragma pack(pop)

#endif//__INET__H__